// dotnet $DOTNET_CSC_DLL -nologo -t:library -r:"../../Backend/System.dll" -r:"../../Backend/System.Collections.dll" -r:"../../The Scroll of Taiwu_Data/Managed/0Harmony.dll" -r:"../../Backend/mscorlib.dll" -r:"../../Backend/netstandard.dll" -r:"../../Backend/GameData.dll" -r:"../../Backend/Redzen.dll" -r:"../../The Scroll of Taiwu_Data/Managed/TaiwuModdingLib.dll" -r:"../../Backend/System.Private.CoreLib.dll" -r:"../../Backend/System.Linq.dll" -r:"../../Backend/System.Runtime.dll" -unsafe -optimize -deterministic Taiwu_EventPackage_Main.cs *.CS *.event.cs -out:Taiwu_EventPackage_Profession.dll -debug
// -r:"../../Backend/System.IO.FileSystem.dll"

//! 编译方法，目前来说只能是自己改上面那行字，把缺的东西都补齐，之后送命令提示符或者用python脚本编译。
//! windows就是一坨shit……连通配符都不支持……不然我至少能写出类似 dotnet C:\Program Files\dotnet\sdk\*\Roslyn\bincore\csc.dll 这样的语法

// -r:"../../The Scroll of Taiwu_Data/Managed/Mono.Cecil.dll" -r:"../../The Scroll of Taiwu_Data/Managed/System.Core.dll"   -r:"../../The Scroll of Taiwu_Data/Managed/System.Composition.AttributedModel.dll" -r:"../../Backend/System.Runtime.dll"
/**
 *  Everyone's Unity Game Plugin
 *  Copyright (C) 2022 Neutron3529
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
//! 这是一个自带文档注释的Mod，虽然代码很shit，但里面有不少注释，或许会对之后准备玩过月逻辑的人有帮助。
//! 所有文档注释会用//!开头，请注意搜索
//! 首先是前后端，前端对后端的调用会先被塞进 ProcessMethodCall，之后分发给对应domain的CallMethod，由CallMethod进行进一步的分发。借助这一点，我们可以快速找到前端调用究竟是在使用哪个函数。
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using static Utils.Logger;
using static Utils.Event;

using Config.EventConfig;
using GameData.Domains.TaiwuEvent;
using GameData.Domains.TaiwuEvent.Enum;

namespace Neutron3529.ChaosEvents;
public partial class ChaosEvents : EventPackage {
    public ChaosEvents() {
        // logger("into ChaosEvents");
        this.NameSpace = "Taiwu.Event.Neutron3529.ChaosEvents";
        this.Author = "Neutron3529";
        this.Group = "ChaosEvents";
        this.EventList = null;
        bool need_init=true;
        foreach(var x in typeof(ChaosEvents).GetMethods()){
            if(x.Name[0]=='E'){
                if(enable(x.Name)){
                    if(need_init){
                        try{
                            this.EventList = new List<TaiwuEventItem>(){
                                new MainTaiwuEvent("ChaosEvents.Base", "70b4d45d-ba15-423d-b94e-b40e348eb9bf"),
                                new ComplexTaiwuEvent("ChaosEvents.Complex", "4e657574-726f-423d-6e33-353239000000")
                            };
                            need_init=false;
                        }catch(Exception ex){
                            logwarn("中子的交互扰乱器：初始化事件有误，这个Mod多半是寄了。错误信息如下：");
                            logex(ex);
                            return;
                        }
                    }
                    // logwarn($"Invoke {x.Name}");
                    try{
                        var o=x.Invoke(this,null);
                        logger($"{o.GetType().Name}({x.Name}) 加载成功");
                        this.EventList.Add((TaiwuEventItem)o);
                    }catch(Exception ex){
                        logwarn($"中子的交互扰乱器：初始化事件{x.Name}有误，这个事件多半是寄了，但不会影响其他事件的正常patch。错误原因如下：");
                        logex(ex);
                    }
                }
            }
        }
        // check valid
        var hs=new HashSet<string>();
        foreach(var e in this.EventList){
            // logwarn(((EventContainer)e).guid);
            foreach(var o in e.EventOptions){
                if(!hs.Contains(o.OptionKey)){
                    hs.Add(o.OptionKey);
                } else {
                    logwarn($"OptionKey冲突：当前事件中已有{o.OptionKey}，这会导致部分选项失效");
                }
            }
        }
    }
}
public class EventContainer : TaiwuEventItem {
    public string guid;
    public EventContainer(string name, string guid){
        this.guid = guid;
        this.Guid = Guid.Parse(guid);
        this.IsHeadEvent = false;
        this.EventGroup = name;
        this.ForceSingle = false;
        this.EventType = EEventType.ModEvent;
        this.TriggerType = EventTrigger.None;
        this.EventSortingOrder = 500;
        this.MainRoleKey = "";
        this.TargetRoleKey = "";
        this.EventBackground = "";
        this.MaskControl = 0;
        this.MaskTweenTime = 0f;
        this.EscOptionKey = "";
        this.EventOptions = null;
    }
    public virtual Func<string> Desc(int i){return ()=>string.Empty;}
    public override bool OnCheckEventCondition()=>true;
    public override void OnEventEnter(){}
    public override void OnEventExit(){}
    public override string GetReplacedContentString()=>string.Empty;
    public override List<string> GetExtraFormatLanguageKeys()=>null;
    public GameData.Common.DataContext context {
        get {
            return GameData.Domains.DomainManager.TaiwuEvent.MainThreadDataContext;
        }
    }
    public string Goto(string eventBody, string option, int abidx=-1, string jumpto=null){
        var ab=abidx==-1?this.ArgBox:this.EventOptions[abidx].ArgBox;
        ab.Set("NeutronEvent",eventBody);
        ab.Set("NeutronOption",option);
        ab.Set("NeutronJumpto",jumpto==null?string.Empty:jumpto);
        return "70b4d45d-ba15-423d-b94e-b40e348eb9bf";
    }
    public static Dictionary<string,int> event_token=new();
    public static List<AutoEvent> events=new();
    public static int AddEvent(AutoEvent e){
        var x=events.Count;
        event_token[e.token]=x;
        events.Add(e);
        return x;
    }
    public string Event(GameData.Domains.TaiwuEvent.EventArgBox argBox, string token){
        if(event_token.TryGetValue(token, out var x))
            return Event(argBox, x);
        logwarn($"事件token {token} 不存在，当前事件将直接结束。请通知Mod作者检查事件链设置是否正确");
        token=string.Empty;
        argBox.Get("NeutronChain",ref token);
        logwarn($"事件链：{token} (this Error)");
        return string.Empty;
    }
    public string Event(string token){
        if(event_token.TryGetValue(token, out var x))
            return Event(x);
        logwarn($"事件token {token} 不存在，当前事件将直接结束。请通知Mod作者检查事件链设置是否正确");
        token=string.Empty;
        this.ArgBox.Get("NeutronChain",ref token);
        logwarn($"事件链：{token} (this Error)");
        return string.Empty;
    }
    public string Event(GameData.Domains.TaiwuEvent.EventArgBox argBox, int token){
        argBox.Set("NeutronIndex",token);
        return "4e657574-726f-423d-6e33-353239000000";
    }
    public string Event(int token){
        this.ArgBox.Set("NeutronIndex",token);
        return "4e657574-726f-423d-6e33-353239000000";
    }
    public static string Name(GameData.Domains.Character.Character ch, int color=0)=>$"<color=#{Colors(color)}FF>{GameData.Domains.TaiwuEvent.EventHelper.EventHelper.GetMonasticTitleOrDisplayName(ch,false)}</color>";
    public static string NameG(GameData.Domains.Character.Character ch)=>Name(ch, 9-ch.GetOrganizationInfo().Grade);
    public static string Grade(string str,int grade)=>$"<color=#{Colors(grade)}FF>{str}</color>";
    public static string Colors(int grade){ //1-9:品阶，0:普通人
        switch (grade) {
            case 0:return "B97D4B";
            case 1:return "E4504D";
            case 2:return "F28234";
            case 3:return "E3C66D";
            case 4:return "AE5AC8";
            case 5:return "63CED0";
            case 6:return "8FBAE7";
            case 7:return "6DB75F";
            case 8:return "FBFBFB";
            default:return "8E8E8E";
        }
    }
}

public class EventItem : EventContainer {
    public EventItem(string name, string guid, string content=null):base(name,guid){
        this.content=content==null?string.Empty:content;
    }
    public string content;
    public string display_content;
    public new virtual string EventContent()=>this.content;
    public override string GetReplacedContentString(){
        this.SetLanguage(new string[]{this.EventContent()});
        // this.EventContent=this.EventContent();
        return string.Empty;
    }
}
public class AutoEvent{
    public string token;
    public int EscIdx;
    public string content;
    public string[] desc;
    public string[] condition_code;
    public bool condition(int i)=>i<this.Count && check_condition(i, condition_code[i]);
    public string[] effect_code=null;
    public string effect(int i)=>i<this.Count?execute_effect(i, effect_code[i]):string.Empty;
    public string[] jumpto=null; // default jumpto.
    public int Count=>desc.Length;
    public string EscOptionKey => "Option_-352900"+this.EscIdx.ToString("02");
    public AutoEvent(string token, string content, string[] desc,int EscIdx=0){
        this.token=token;
        this.content=content;
        this.desc=desc;
        this.EscIdx=EscIdx;
        // 理论上jumpto和effect_code可以二选一，使用以null初始化，而后使用函数进行进一步初始化。
        this.jumpto=new string[this.Count];
        this.effect_code=new string[this.Count];
        this.condition_code=new string[this.Count];
        Array.Fill(this.jumpto,string.Empty);
        Array.Fill(this.effect_code,string.Empty);
        Array.Fill(this.condition_code,string.Empty);
    }
    public AutoEvent(string token, string content, string[] desc, string[] jumpto, int EscIdx=0){
        if(desc.Length!=jumpto.Length){
            throw new Exception($"输入长度不匹配,desc.Length={desc.Length},jumpto.Length={jumpto.Length}");
        }
        this.token=token;
        this.content=content;
        this.desc=desc;
        this.EscIdx=EscIdx;
        // 理论上jumpto和effect_code可以二选一，使用以null初始化，而后使用函数进行进一步初始化。
        this.jumpto=jumpto;
        this.effect_code=new string[this.Count];
        this.condition_code=new string[this.Count];
        // Array.Fill(this.jumpto,string.Empty);
        Array.Fill(this.effect_code,string.Empty);
        Array.Fill(this.condition_code,string.Empty);
    }
    public AutoEvent(string token, string content, string[] desc, string[] effect_code, string[] condition_code, int EscIdx=0){
        if(desc.Length!=effect_code.Length || desc.Length!=condition_code.Length){
            throw new Exception($"输入长度不匹配,desc.Length={desc.Length},effect_code.Length={effect_code.Length},condition_code.Length={condition_code.Length}");
        }
        this.token=token;
        this.content=content;
        this.desc=desc;
        this.EscIdx=EscIdx;
        // 理论上jumpto和effect_code可以二选一，使用以null初始化，而后使用函数进行进一步初始化。
        this.jumpto=new string[this.Count];
        this.effect_code=effect_code;
        this.condition_code=condition_code;
        Array.Fill(this.jumpto,string.Empty);
        // Array.Fill(this.effect_code,string.Empty);
        // Array.Fill(this.condition_code,string.Empty);
    }
    public AutoEvent(string token, string content, string[] desc, string[] jumpto, string[] effect_code, string[] condition_code, int EscIdx=0){
        if(desc.Length!=jumpto.Length || desc.Length!=effect_code.Length || desc.Length!=condition_code.Length){
            throw new Exception($"输入长度不匹配,desc.Length={desc.Length},jumpto.Length={jumpto.Length},effect_code.Length={effect_code.Length},condition_code.Length={condition_code.Length}");
        }
        this.token=token;
        this.content=content;
        this.desc=desc;
        this.EscIdx=EscIdx;
        this.jumpto=jumpto;
        this.effect_code=effect_code;
        this.condition_code=condition_code;
    }
    static IEnumerable<string[]> process(string code){
        foreach(var c in code.Split(';'))
            yield return c.Trim().Split(' ',StringSplitOptions.RemoveEmptyEntries);
    }
    bool check_condition(int index, string code){
        try{
            foreach(var x in process(code)){
                // TODO
            }
        }catch(Exception e){
            logwarn($"事件条件检查出错, 出错事件为{this.token}中index为{index}的选项，出错代码如下：\n{code}\n请联系Mod作者修复。");
            logex(e);
            return false;
        }
        return this.desc[index]!=string.Empty;
    }
    string execute_effect(int index, string code){ // return jumpto if it is dynamic.
        try{
            foreach(var x in process(code)){
                // TODO
            }
        }catch(Exception e){
            logwarn($"事件执行出错，停止后续事件执行。出错事件为{this.token}中index为{index}的选项，出错代码如下：\n{code}\n请联系Mod作者修复。");
            logex(e);
            return string.Empty;
        }
        return this.jumpto[index];
    }
}
public class ComplexTaiwuEvent : EventItem {
    new AutoEvent Event=null;
    public ComplexTaiwuEvent(string name, string guid):base(name,guid){
        this.EscOptionKey = "Option_-35290000";
        this.EventOptions=new TaiwuEventOption[100];
        for(int j=0;j<100;j++){
            int i=j;
            this.EventOptions[i]=new TaiwuEventOption{
                OptionKey = "Option_-352900"+i.ToString("02"),
                OnOptionSelect = this.OnOptionSelect(i),
                OnOptionVisibleCheck = this.OnOptionVisibleCheck(i),
                OnOptionAvailableCheck = ()=>true,
                GetReplacedContent = this.Desc(i),
                GetExtraFormatLanguageKeys = this.GetExtraFormatLanguageKeys
            };
        };
        this.content=string.Empty;
    }
    public Func<bool> OnOptionVisibleCheck(int i)=>()=>this.Event.condition(i);
    public override void OnEventEnter(){
        int index=0;
        if(this.ArgBox.Get("NeutronIndex",ref index)){
            this.ArgBox.Remove<int>("NeutronIndex");
            this.Event=EventContainer.events[index];
        } else {
            string token=string.Empty;
            if(!this.ArgBox.Get("NeutronToken",ref token)){
                logwarn("OnEventEnter failed.");
            }
            this.ArgBox.Remove<int>("NeutronToken");
            this.Event=EventContainer.events[EventContainer.event_token[token]];
        }
        this.EscOptionKey=this.Event.EscOptionKey;
        // logwarn("replace check");
        // logger(this.Event.token);
        // logger(this.Event.content);
        // logger(this.Event.desc[0]);
    }
    public override void OnEventExit(){}
    public override string EventContent(){
        return this.Event.content;
    }
    new Func<string> Desc(int i)=>()=>{
        this.EventOptions[i].SetContent(this.Event.desc[i]);
        return string.Empty;
    };
    Func<string> OnOptionSelect(int i){
        return ()=>{
            var to=this.Event.effect(i);
            if(EventContainer.event_token.ContainsKey(to)){
                string x=string.Empty;
                this.ArgBox.Get("NeutronChain",ref x);
                this.ArgBox.Set("NeutronChain",x + to + " -> ");
                return Event(this.ArgBox, to);
            } else {
                string x=string.Empty;
                this.ArgBox.Get("NeutronChain",ref x);
                logger("neutron chain finished: "+x+(to==string.Empty?"(finish)":to));
                this.ArgBox.Remove<string>("NeutronChain");
                return to;
            }
        };
    }
    // public override string GetReplacedContentString(){
    //     logwarn("replace check");
    //     logger(this.Event.token);
    //     logger(this.Event.content);
    //     logger(this.Event.desc[0]);
    //     return base.GetReplacedContentString();
    // }
}
public class MainTaiwuEvent : EventItem {
    public MainTaiwuEvent(string name, string guid):base(name,guid){
        this.EscOptionKey = "Option_35290000";
        this.EventOptions=new TaiwuEventOption[1]{new TaiwuEventOption{
            OptionKey = "Option_35290000",
            OnOptionSelect = new Func<string>(this.OnOptionSelect),
            OnOptionVisibleCheck = new Func<bool>(this.OnCheckEventCondition),
            OnOptionAvailableCheck = new Func<bool>(this.OnCheckEventCondition),
            GetReplacedContent = new Func<string>(this.Desc),
            GetExtraFormatLanguageKeys = new Func<List<string>>(this.GetExtraFormatLanguageKeys)
        }};
        this.content=string.Empty;
    }
    bool erase=true;
    public override bool OnCheckEventCondition()=>true;
    public override void OnEventExit(){
        if(this.erase){
            this.ArgBox.Remove<string>("NeutronEvent");
            this.ArgBox.Remove<string>("NeutronOption");
            this.ArgBox.Remove<string>("NeutronJumpto");
        } else this.erase=true;
    }
    public override string EventContent(){
        this.ArgBox.Get("NeutronEvent",ref this.content);
        return this.content;
    }
    string Desc(){
        string x=string.Empty;
        this.ArgBox.Get("NeutronOption",ref x);
        return x;
    }
    string OnOptionSelect(){
        string x=string.Empty;
        this.ArgBox.Get("NeutronJumpto",ref x);
        if(x.Contains("\t")){
            this.erase=false;
            var z=x.Split("\t");
            if(z.Length>1){
                this.ArgBox.Set("NeutronEvent",z[0]);
                this.ArgBox.Set("NeutronOption",z[1]);
                this.ArgBox.Set("NeutronJumpto",string.Join("\t",z[2..]));
                return "70b4d45d-ba15-423d-b94e-b40e348eb9bf";
            }
        }
        return x;
    }
}
public class UnfairTrue : Redzen.Random.IRandomSource {
    public UnfairTrue(){}
    public void Reinitialise(ulong seed){}
    public int Next()=>0;
    public int Next(int maxValue)=>-2147483648;
    public int Next(int minValue, int maxValue)=>minValue;
    public double NextDouble()=>0;
    public void NextBytes(Span<byte> span){}
    public int NextInt()=>0;
    public uint NextUInt()=>0;
    public ulong NextULong()=>0;
    public bool NextBool()=>true;
    public byte NextByte()=>0;
    public float NextFloat()=>0f;
    public float NextFloatNonZero()=>1f;
    public double NextDoubleNonZero()=>1.0;
    public double NextDoubleHighRes()=>1.0;
}
